//
// Buffer.cpp
// libprotea
// Tools Library for the Protea Project / Buffer functionality
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

//
// Buffer.cpp : Implementation of XCBuffer
//
#include "../defines.h"
#include "../Fields/fields.h"
#include "../AppHandler/AppInterface.h"
#include "../AppHandler/Transactions.h"
#include "../Security/base64.h"
#include "../Security/hash.h"
#include "Buffer.h"
#include <stdarg.h>

#include "../defns.h"
using namespace ProteaApp1;

/////
//
// Memory tracking for buffers
//
/////
class AllocatedBufferInfo {
public:
	XCBuffer* object;
	AllocatedBufferInfo* next;
};

AllocatedBufferInfo* allocated_buffers = NULL;

void BufferAdd( XCBuffer* buffer ) {
	AllocatedBufferInfo* temp = new AllocatedBufferInfo;
	temp->object = buffer;
	temp->next = allocated_buffers;
	allocated_buffers = temp;
};

void BufferRemove( XCBufferList* list ) {
	for (int i=0; i<list->buffercount; i++)
		BufferRemove( list->GetBuffer( i ) );
};

void BufferRemove( XCBuffer* buffer ) {
	AllocatedBufferInfo* temp = allocated_buffers;

	int count = 0;

	if (temp) {
		// Is it the first item?
		if (temp->object == buffer) {
			allocated_buffers = temp->next;
			delete temp;
		} else {
			// Search for the object
			while (temp->next && temp->next->object != buffer) {
				count++;
				temp = temp->next;
			};

			// The object should be in the list, except for stated txns
			if (temp->next) {
				AllocatedBufferInfo* deleteme = temp->next;
				temp->next = temp->next->next;
				delete deleteme;
			};
		};
	};
};

void BufferDump( XCAppInterface* app, XCTransactionInfo* info ) {
	bool first = true;
	AllocatedBufferInfo* temp = allocated_buffers;
	while (temp) {
		if (first) {
			if (info) {
				transaction_defn* txn_code = app->GetTransaction( info->GetTransNumber() );
				printf( "UNRELEASED BUFFERS: %s  [TransID=%ld, Phase=%d]", txn_code->trans_code, info->GetTransID(), info->GetPhase() );
			} else
				printf( "UNRELEASED BUFFERS:" );
			first = false;
		};
		printf( "\t%s", temp->object->GetFormatName() );
		temp = temp->next;
	};

	if (!first)
		printf( "\n" );

	BufferClear();
};

void BufferClear() {
	// TODO: Cleanup memory here
	allocated_buffers = NULL;
};




/////////////////////////////////////////////////////////////////////////////
//
// XCBuffer
//
/////////////////////////////////////////////////////////////////////////////
XCBuffer::XCBuffer() {
	IsDynamic = false;
	IsDynamicFields = false;
	HasBLOB = false;

	FieldList = NULL;
	field_index = NULL;
	data = NULL;

	BUFFER_ADDREF( this );
}


XCBuffer::~XCBuffer() {
	if (FieldList)
		delete [] FieldList;

	if (field_index)
		delete [] field_index;

	if (data)
		delete [] data;

	if (IsDynamicFields) {
		buffer_ds* records = (buffer_ds*)APPDIC->FixOffset( mydesc->ds_offset );
		int count = mydesc->ds_count;
		while (count) {
			buffer_field* fieldptr = (buffer_field*)APPDIC->FixOffset( records->field_offset );
			delete fieldptr;
			count--;
			records++;
		};
	};

	if (IsDynamic) {
		buffer_ds* records = (buffer_ds*)APPDIC->FixOffset( mydesc->ds_offset );
		delete [] records;
		delete mydesc;
	};

	BUFFER_RELEASE( this );
};



XCBuffer* XCBuffer::Copy() const {
	INFUNC( XCBuffer::Copy, APPDIC );

	// Create a new buffer with the same format number
	XCBuffer* result = new XCBuffer();
	result->Create( mydesc->number, false, APPDIC );

	// Copy data from this buffer to new buffer
	memcpy( result->data, data, mydesc->length );

	OUTFUNCRET( result );
};




void XCBuffer::LoadDefaults()
{
	INFUNC( XCBuffer::LoadDefaults, APPDIC );

	APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "(%d-%s)  Load buffer defaults", mydesc->number, mydesc->name );

	if (mydesc->default_offset) {
		bool old_tracing = APPDIC->tracing;
		APPDIC->tracing = false;

		// Ask each field to load it's default value
		XCField* temp = FieldList;
		for (int i=0; i<mydesc->ds_count; i++) {
			temp->SetDefault();
			temp++;
		};

		APPDIC->tracing = old_tracing;
	} else
		RAISETOOLSERROR( ERR_INV_BUFFER_DEFAULTS );
 
	OUTFUNC();
}


void XCBuffer::FillDefaults() {
	INFUNC( XCBuffer::FillDefaults, APPDIC );

	XCField* temp = FieldList;

	bool old_tracing = APPDIC->tracing;
	APPDIC->tracing = false;

	// For each field, if blank, fill default value in
	for (int i=0; i<mydesc->ds_count; i++) {
		if (temp->IsBlank())
			temp->SetDefault();
		temp++;
	};

	APPDIC->tracing = old_tracing;

	OUTFUNC();
};


XCField* XCBuffer::GetFieldByIndex(long index) const
{
	APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "(%d)  GetFieldByIndex: %d", mydesc->number, index );
	if (index < mydesc->ds_count)
		return &FieldList[ index ];
	else
		return NULL;
}



XCField* XCBuffer::GetField(long FieldNumber) const
{
	INFUNC( XCBuffer::GetField, APPDIC );

	int i;

	APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "(%d)  Get Field: %d", mydesc->number, FieldNumber );

	if (FieldList) {
		if (field_index) {
			// If this is indexed, use a binary search
			int first = 0;
			int last = mydesc->ds_count-1;

			// Loop until we cross
			while (first <= last) {
				int loc = (first + last) / 2;
				int value = field_index[ loc ].fld_nbr;
				if (FieldNumber < value) {
					// Search first half
					last = loc - 1;
				} else if (FieldNumber > value) {
					// Search last half
					first = loc + 1;
				} else
					RETURNFUNC( &FieldList[ field_index[ loc ].record_num ] );
			};
		} else {
			// Otherwise, do a sequential search
			for (i = 0; i<mydesc->ds_count; i++) {
				if (FieldList[i].number == FieldNumber) {
					APPDIC->ToolsTrace( TRC_BUFFER, TLV_EVERYTHING, "\tFound Field: %s", FieldList[i].GetFieldName() );
					RETURNFUNC( &FieldList[i] );
				};
			};
		};
	};

	APPDIC->ToolsTrace( TRC_BUFFER, TLV_EVERYTHING, "\tDoesn't exist" );

	OUTFUNCRET( NULL );
}



char* XCBuffer::GetFormatName()
{
	APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "(%d)  Get Format Name - %-16.16s", mydesc->number, mydesc->name );
	return mydesc->name;
}





buffer_db_defn* XCBuffer::GetDatabaseInfo()
{
	if (mydesc->dd_offset) {
		buffer_db_tables* table = (buffer_db_tables*)(mydesc->dd_offset + APPDIC->APPDIC_DATA);
		return (buffer_db_defn*)(table->db_offset + APPDIC->APPDIC_DATA);
	} else
		return NULL;
}




void XCBuffer::MoveCorresponding(const XCBuffer *source, XCAppErrorHandler* ErrorList, const bool MoveBlanks)
{
	int i, j;
	bool errors = false;
	XCField* temp;
	XCField* myfield = FieldList;

	INFUNC( XCBuffer::MoveCorresponding, APPDIC );

	// Check for valid source buffer
	if (!source)
		RAISETOOLSERROR_1PARAM( ERR_INVALID_PARAMETER, 1 );

	APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "(%d-%s)  Move corresponding from (%d-%s)", mydesc->number, mydesc->name, source->mydesc->number, source->mydesc->name );
	source->DebugDump();

	if (field_index && source->field_index) {
		// If we have a field index, use it to optimize
		int source_index = 0;
		int target_index = 0;
		int source_count = source->mydesc->ds_count;
		while (target_index < mydesc->ds_count && source_index < source_count) {
			// Stash the field we are looking for
			int number = field_index[ target_index ].fld_nbr;

			// While the source field number is less, move on
			while (source_index < source_count && source->field_index[ source_index ].fld_nbr < number)
				source_index++;

			// Did we find it?
			if (source_index < source_count && source->field_index[ source_index ].fld_nbr == number) {
				// Attempt to move the field, fail on invalid field.
				try {
					FieldList[ field_index[ target_index ].record_num ].MoveField(
						&source->FieldList[ source->field_index[ source_index ].record_num ],
						MoveBlanks );
				} catch( XCToolsError& error ) {
					// Write a trace message
					APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "(%d)  Move failed", mydesc->number );

					if (ErrorList) {
						ErrorList->Add( error );
						errors = true;
					} else
						throw;
				} catch( ... ) {
					// Continue to throw the error
					throw;
				};

				// Move on to the next one
				source_index++;
			};
				
			target_index++;
		};
	} else {
		// Otherwise, sequential
		for (i=0; i<mydesc->ds_count; i++) {
			long mynumber = myfield->number;

			APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "  Source field: %s", myfield->GetFieldName() );

			temp = source->FieldList;
			for (j=0; j<source->mydesc->ds_count; j++) {
				if (temp->number == mynumber) {		// We have a match, so move it over
					// Attempt to move the field, fail on invalid field.
					try {
						myfield->MoveField( temp, MoveBlanks );
					} catch( XCToolsError& error ) {
						// Write a trace message
						APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "(%d)  Move failed", mydesc->number );

						if (ErrorList) {
							ErrorList->Add( error );
							errors = true;
						} else
							throw;
					} catch( ... ) {
						// Continue to throw the error
						throw;
					};
					j = source->mydesc->ds_count;
				};
				temp++;
			};
			myfield++;
		};
	};

	DebugDump();

	OUTFUNC();
}



void XCBuffer::MoveCorrespondingExchange( const XCBuffer* source, XCBuffer* exchange, XCAppErrorHandler* ErrorList ) {
	// Move from source to exchange buffer
	exchange->MoveCorresponding( source, ErrorList );

	// Move from exchange buffer to target buffer
	MoveCorresponding( exchange, ErrorList );
};



void XCBuffer::MoveField(const XCBuffer *source, const long FieldNumber, const bool MoveBlanks)
{
	INFUNC( XCBuffer::MoveField, APPDIC )

	XCField* source_fld, *target_fld;

	if (!source || source == this)
		RAISETOOLSERROR_1PARAM( ERR_INVALID_PARAMETER, 1 );

	APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "(%d)  Move field", mydesc->number );

	source_fld = source->GetField( FieldNumber );
	target_fld = GetField( FieldNumber );

	if (source_fld && target_fld)
		target_fld->MoveField( source_fld, MoveBlanks );

	OUTFUNC()
}




void XCBuffer::MoveUnlikeField( const long tgt_fld, const XCBuffer *source, const long src_fld, const bool MoveBlanks ) {
	INFUNC( XCBuffer::MoveUnlikeField, APPDIC )

	XCField *source_fld, *target_fld;

	APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "(%d)  Move Unlike", mydesc->number );

	// Check target field
	target_fld = GetField( tgt_fld );
	if (!target_fld)
		RAISETOOLSERROR_1PARAM( ERR_INVALID_PARAMETER, 1 );

	// Check source field
	source_fld = source->GetField( src_fld );
	if (!source_fld)
		RAISETOOLSERROR_1PARAM( ERR_INVALID_PARAMETER, 3 );

	target_fld->MoveField( source_fld, MoveBlanks );

	OUTFUNC()
};




int XCBuffer::CreateFromData( long FormatNumber, void* new_data, XCAppInterface* new_owner ) {
	INFUNC( XCBuffer::CreateFromData, new_owner );

	int result;

	int i;
	unsigned char *cursor;

	APPDIC = new_owner;

	mydesc = APPDIC->GetFormatInfo( FormatNumber );
	if (!mydesc) {
		APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "(%d)  Unable to find descriptor record", FormatNumber );
		RAISETOOLSERROR( ERR_FMT_NOT_FOUND );
	};

	result = PrepareBuffer( 2 );
	if (!result) 
		memcpy( data, new_data, mydesc->length );

	//now check for BLOBS real quick

	cursor = (unsigned char *)new_data + mydesc->length;
	long BLOBSize;

	for( i=0; i<GetFieldCount(); i++ ) 
	{
		if( FieldList[i].GetType() == XCBLOB ) 
		{
			HasBLOB = true;
			BLOBSize = FieldList[i].GetBLOBSize(true);
			if( BLOBSize > 0 ) 
			{
				// This allocates memory for the BLOB, sets BLOB length, and pointer to BLOB memory
				// It then copies the actual BLOB into the BLOB memory	
				FieldList[i].SetBLOB( BLOBSize, (long int *)cursor );
				cursor += BLOBSize;
//				cursor += ( BLOBSize / sizeof( long* ) );
			} 
			else 
			{
				*(long *)FieldList[i].GetOffset() = 0;
			}
		}
	}

	OUTFUNCRET( result );
};





int XCBuffer::Create(long FormatNumber, bool load_defaults, XCAppInterface* new_owner)
{
	INFUNC( XCBuffer::Create, new_owner )

	APPDIC = new_owner;

	mydesc = APPDIC->GetFormatInfo( FormatNumber );
	if (!mydesc) {
		APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "(%d)  Unable to find descriptor record", FormatNumber );
		RAISETOOLSERROR( ERR_FMT_NOT_FOUND );
	};

	OUTFUNCRET( PrepareBuffer( load_defaults ? 1 : 0 ) )
};




void SetLength( buffer_ds* current, buffer_field* field ) {
	int type = field ? field->type : current->data_type;

	switch( type ) {
		case S_INTEGER:
			current->alloc_size = current->max_length = sizeof( short int );
			break;

		case LITERAL_STRING:
			// Reset the data type to LITERAL_VAL
			current->data_type = LITERAL_VAL;

			// Fall-thru intentional

		case D_INTEGER:
		case AUTONUMBER:
		case LITERAL_VAL:
			current->alloc_size = current->max_length = sizeof( long );
			break;

		case DEC_DOUBLE:
		case DEC_FLOAT:
			current->alloc_size = current->max_length = sizeof( double );
			break;

		default:
			if (field) {
				current->alloc_size = field->length + 1;
				current->max_length = field->length;
			} else {
				current->alloc_size = current->max_length + 1;
			};
	};
};



int XCBuffer::CreateDynamic(const long *FieldNumbers, bool Load_Defaults, long FormatNumber, XCAppInterface* new_owner)
{
	INFUNC( XCBuffer::CreateDynamic, new_owner )
	const long* temp = FieldNumbers;

	assert( new_owner );
	APPDIC = new_owner;
	APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "Create dynamic buffer" );

	IsDynamic = true;

	mydesc = new buffer_format;
	memset( mydesc, 0, sizeof( buffer_format ) );

	if (FormatNumber) {
		buffer_format* temp = APPDIC->GetFormatInfo( FormatNumber );
		if (temp) {
			memcpy( mydesc, temp, sizeof( buffer_format ) );
			mydesc->indexed = false;
		} else
			RAISETOOLSERROR( ERR_INV_FORMAT_NBR );
	} else {
		strcpy( mydesc->name, "UNNAMED" );
		mydesc->number = FormatNumber;
	};

	mydesc->ds_count = 0;
	while (temp[ mydesc->ds_count ] != 0)
		mydesc->ds_count++;

	// Make temporary data structures records now...
	buffer_ds* records = new buffer_ds[ mydesc->ds_count ];

	// Make a fake offset
	mydesc->ds_offset = (long)((char*)records - new_owner->APPDIC_DATA);

	for (int i=0; i<mydesc->ds_count; i++) {
		buffer_ds* current = &records[i];
		buffer_field* field = new_owner->GetFieldInfo( temp[i] );
		if (!field) {
			APPDIC->ToolsTrace( TRC_APPINTERFACE, TLV_LOW, "Invalid field number: %d", temp[i] );
			RAISETOOLSERROR( ERR_INV_FIELD_NBR );
		};

		memset( current, 0, sizeof( buffer_ds ) );
		current->data_type = field->type;
		current->dec_places = field->decimal_places;
		current->min_length = 0;
		current->data_offset = mydesc->length;

		// Set the max and allocated lengths
		SetLength( current, field );

		current->field_offset = (long)((char*)field - new_owner->APPDIC_DATA);

		mydesc->length += Align( current->alloc_size );
	};

	OUTFUNCRET( PrepareBuffer( 0 ) )
}





int XCBuffer::CreateDynamic( int count, const buffer_ds* Fields, long FormatNumber, XCAppInterface* new_owner ) {
	INFUNC( XCBuffer::CreateDynamic, new_owner )

	assert( new_owner );
	APPDIC = new_owner;
	APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "Create dynamic buffer (manually)" );

	IsDynamic = true;

	mydesc = new buffer_format;
	memset( mydesc, 0, sizeof( buffer_format ) );

	if (FormatNumber) {
		buffer_format* temp = APPDIC->GetFormatInfo( FormatNumber );
		if (temp) {
			memcpy( mydesc, temp, sizeof( buffer_format ) );
			mydesc->indexed = false;
		};
	} else {
		mydesc->number = FormatNumber;
		strcpy( mydesc->name, "UNNAMED" );
		mydesc->type = 4;
	};

	mydesc->ds_count = count;

	// Make temporary data structures records now...
	buffer_ds* records = new buffer_ds[ count ];
	memcpy( records, Fields, sizeof( buffer_ds ) * count );

	// Make a fake offset
	mydesc->ds_offset = (long)((char*)records - new_owner->APPDIC_DATA);

	buffer_ds* current = records;
	for (int i=0; i<count; i++) {
		SetLength( current, NULL );
		mydesc->length += Align( current->alloc_size );

		records++;
	};

	OUTFUNCRET( PrepareBuffer( 0 ) )
};


int XCBuffer::CreateDynamic( buffer_format* fmt, XCAppInterface* new_owner )
{
	INFUNC( XCBuffer::CreateDynamic, new_owner )

	assert( new_owner );
	assert( fmt );
	APPDIC = new_owner;
	APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "Create dynamic buffer (manually)" );

	IsDynamic = true;
	IsDynamicFields = true;

	mydesc = fmt;

	OUTFUNCRET( PrepareBuffer( 0 ) )	
};


//
// PrepareBuffer
//
//	Creates the actual buffer space where all field data will reside.  Also sets position
//	pointer for each field
//
int XCBuffer::PrepareBuffer( int load_mode )
{
	INFUNC( XCBuffer::PrepareBuffer, APPDIC )
	int i;

	APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "(%d)  Load buffer information - %-16.16s", mydesc->number, mydesc->name );


	buffer_ds* field = (buffer_ds*)APPDIC->FixOffset( mydesc->ds_offset );
	if (field && mydesc->ds_count >= 0 && mydesc->length >= 0) {
		APPDIC->ToolsTrace( TRC_BUFFER, TLV_EVERYTHING, "  Field count: %d, Buffer size = %d", mydesc->ds_count, mydesc->length );

		// Allocate our field info.
		FieldList = new XCField[ mydesc->ds_count ];
		for (i = 0; i<mydesc->ds_count; i++) {
			FieldList[ i ].LoadStructureData( field, this );
			field++;
		};

		// Allocate the index structure
		if (mydesc->indexed) {
			field_index_t* temp_index;
			field_index = new field_index_t[ mydesc->ds_count ];
			XCField* fld = FieldList;
			for (i=0; i<mydesc->ds_count; i++) {
				int index = fld->mydesc->index;
				temp_index = &field_index[ index ];

				temp_index->fld_nbr = fld->GetFieldNumber();
				temp_index->record_num = i;
				fld++;
			};
		} else {
			field_index = NULL;
		};

		data = new char[ mydesc->length ];

		// Adjust to point to data buffer area
		for (i = 0; i<mydesc->ds_count; i++)
			FieldList[i].buffer_offset += (long)data;

		// Now handle load mode
		switch( load_mode ) {
			case 0:		// Blank
				memset( data, 0, mydesc->length );
				break;

			case 1:		// Use defaults
				LoadDefaults();
				break;

			default:	// Uninitialized
				break;
		};
	} else {
		FieldList = NULL;
		data = NULL;
	};

	OUTFUNCRET( 0 );
}





void XCBuffer::DebugDump() const
{
	INFUNC( XCBuffer::DebugDump, APPDIC );

	const long cutoff = 20;

	unsigned char temp[ cutoff + 1 ];
	char temp2[ 256 ];

	int count = mydesc->length;
	int position = 0;
	int i, j;

	bool oldtrace = APPDIC->tracing;

	APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "(%d)  DebugDump: %-16.16s", mydesc->number, mydesc->name );

	if (APPDIC->options.debug_level_cutoff[ APPDIC->GetTraceIndex( TRC_NOLABEL ) ] == TLV_EVERYTHING) {

		while (count) {
			int mylen;


			if (count > cutoff)
				mylen = cutoff;
			else
				mylen = count;

			memcpy( temp, &data[ position ], mylen );
			if (mylen < cutoff)
				memset( &temp[ mylen ], 0, cutoff - mylen );

			// Create the hex version
			j = 0;
			for (i=0; i<cutoff; i++) {
				if (i < mylen) {
					j += sprintf( &temp2[j], "%2.2X ", temp[i] );
				} else
					j += sprintf( &temp2[j], "   " );
			};

			// Fix non-printable characters
			for (i=0; i<mylen; i++) {
				if (temp[i] == 0)
					temp[i] = '~';
				else if (temp[i] < 32)
					temp[i] = 32;
			};
			temp[mylen] = 0;

			// Output everything
			APPDIC->ToolsTrace( TRC_NOLABEL, TLV_HIGH, "  %4d: %s %s", position, temp2, temp );

			count -= mylen;
			position += mylen;
		};
	} else {
		int count = 0;
		int max = GetFieldCount();
		XCcrc32 BlobCRC;
		long crc;

		XCField* field = FieldList;
		APPDIC->tracing = false;
		while (count < max) {
			char changed = '-';
			if (field->IsChanged())
				changed = '*';

			i = sprintf( temp2, "  %c %-20.20s (%2.2d) = ", changed, field->GetFieldName(), field->GetType() );

			switch( field->GetType() ) {
				case S_INTEGER:
				case D_INTEGER:
				case LITERAL_VAL:
					sprintf( &temp2[i], "%d", field->GetLong( true ) );
					break;

				case DEC_FLOAT:
				case DEC_DOUBLE:
					sprintf( &temp2[i], "%f", field->GetDouble( true ) );
//					sprintf( &temp2[i], "-%d.%df", field->GetLength()+2, field->GetDecPlaces() );
					break;

				case XCBOOL:
					if (field->IsBlank())
						sprintf( &temp2[i], "[BLANK]" );
					else if (*field->GetOffset() == '1')
						sprintf( &temp2[i], "True" );
					else if (*field->GetOffset() == '0')
						sprintf( &temp2[i], "False" );
					else
						sprintf( &temp2[i], "INVALID  '%s'", field->GetOffset() );
					break;

				case XCBLOB:
					crc = BlobCRC.Get_CRC32( (unsigned char *)field->GetBLOB(), field->GetBLOBSize() );
					sprintf( &temp2[i], "Size: %ld, Loc: 0x%x, CRC: %ld", field->GetBLOBSize(), field->GetBLOB(), crc);
					break;

				default: {
					char* tempval = field->GetOffset();
					if (*tempval) {
						snprintf( &temp2[i], 255 - i, "'%s'", field->GetOffset() );
					} else
						sprintf( &temp2[i], "NULL" );
					break;
				};
			};

			APPDIC->tracing = true;
			APPDIC->ToolsTrace( TRC_NOLABEL, TLV_HIGH, "%s", temp2 );
			APPDIC->tracing = false;

			field++;
			count++;
		};
	};

	APPDIC->tracing = oldtrace;

	OUTFUNC();
};



bool XCBuffer::IsBlank()
{
	INFUNC( XCBuffer::IsBlank, APPDIC );

	for (int i=0; i<mydesc->ds_count; i++) {
		if (!FieldList[i].IsBlank())
			RETURNFUNC( false );
	};

	OUTFUNCRET( true );
};



bool XCBuffer::IsBlank( int count, ... ) {
	INFUNC( XCBuffer::IsBlank, APPDIC );

	va_list parmlist;
	int i;

	XCField* tempfield = FieldList;
	for (i=0; i<mydesc->ds_count; i++) {
		bool useme = true;

		// Check to see if we should skip this one
		va_start( parmlist, count );
		for (int j=0; j<count; j++) {
			long temp = va_arg( parmlist, long );
			if (temp == tempfield->GetFieldNumber()) {
				useme = false;
				break;
			};
		};
		va_end( parmlist );


		if (useme && !tempfield->IsBlank())
			RETURNFUNC( false );

		tempfield++;
	};

	OUTFUNCRET( true );
};


void XCBuffer::ResetChanges( bool defaultval ) {
	INFUNC( XCBuffer::ResetChanges, APPDIC );

	XCField* temp = FieldList;
	for (int i = 0; i<GetFieldCount(); i++) {
		temp->SetChange( defaultval );
		temp++;
	};

	OUTFUNC();
};



bool XCBuffer::MarkChanges( const XCBuffer* compare ) {
	bool retval = false;
	EXT_INFUNC( XCBuffer::MarkChanges, APPDIC );

	// Buffers must have the format number
	if (GetFormatNumber() != compare->GetFormatNumber())
		RAISETOOLSERROR_1PARAM( ERR_INVALID_PARAMETER, 1 );

	XCField* sfield = FieldList;
	XCField* tfield = compare->FieldList;

	for (int i=0; i<GetFieldCount(); i++) {
		if (sfield->CompareField( tfield ) == 0)
			sfield->SetChange( false );
		else
		{
			sfield->SetChange( true );
			retval = true;
		};

		sfield++;
		tfield++;
	};

	EXT_OUTFUNCRET( retval );
};



bool XCBuffer::IsChanged() {
	INFUNC( XCBuffer::IsChanged, APPDIC );

	XCField* sfield = FieldList;

	for (int i=0; i<GetFieldCount(); i++) {
		if (sfield->IsChanged())
			return true;
		sfield++;
	};

	OUTFUNCRET( false );
};



bool XCBuffer::IsChanged( long field_number, XCBuffer* comparebuffer )
{
	INFUNC( XCBuffer::IsChanged, APPDIC );

	XCField* temp1, *temp2;

	temp1 = GetField( field_number );
	temp2 = comparebuffer->GetField( field_number );

	if (!temp1 || !temp2)
		RETURNFUNC( false );

	OUTFUNCRET( temp1->CompareField( temp2 ) != 0 );
};




void XCBuffer::Clear()
{
	INFUNC( XCBuffer::Clear, APPDIC );

	for (int i=0; i<mydesc->ds_count; i++)
		FieldList[i].Clear();

	OUTFUNC();
};


long XCBuffer::CompareFields( const long Field1, const long Field2 ) const {
	return GetField( Field1 )->CompareField( GetField( Field2 ) );
};


void XCBuffer::AddFields( const long parm1, const long parm2, const long result ) {
	INFUNC( XCBuffer::AddFields, APPDIC )

	XCField* field1 = GetField( parm1 );
	XCField* field2 = GetField( parm2 );
	XCField* rfield = GetField( result );

	if (!field1)
		RAISETOOLSERROR_1PARAM( ERR_FLD_NOT_FOUND, field1 );
	if (!field2)
		RAISETOOLSERROR_1PARAM( ERR_FLD_NOT_FOUND, field2 );
	if (!rfield)
		RAISETOOLSERROR_1PARAM( ERR_FLD_NOT_FOUND, rfield );

	rfield->Add( field1, field2 );

	OUTFUNC()
}


void XCBuffer::AddCorresponding( const XCBuffer* source ) {
	INFUNC( XCBuffer::AddCorresponding, APPDIC )

	int i, j;
	XCField* temp;

	if (!source)
		RAISETOOLSERROR_1PARAM( ERR_INVALID_PARAMETER, 1 );

	APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "(%d-%s)  Add corresponding from (%d-%s)", mydesc->number, mydesc->name, source->mydesc->number, source->mydesc->name );

	for (i=0; i<mydesc->ds_count; i++) {
		long mynumber = FieldList[i].number;

		APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "  Source field: %s", FieldList[i].GetFieldName() );

		temp = source->FieldList;
		for (j=0; j<source->mydesc->ds_count; j++) {
			if (temp->number == mynumber) {		// We have a match, so move it over
				// Attempt to move the field, fail on invalid field.
				FieldList[i].Add( temp );
			};
			temp++;
		};
	};

	OUTFUNC()
};


void XCBuffer::AddCorrespondingExchange( const XCBuffer* source, XCBuffer* exchange, XCAppErrorHandler* err ) {
	INFUNC( XCBuffer::AddCorrespondingExchange, APPDIC )

	// Check for valid parameters
	if (!source)
		RAISETOOLSERROR_1PARAM( ERR_INVALID_PARAMETER, 1 );
	if (!exchange)
		RAISETOOLSERROR_1PARAM( ERR_INVALID_PARAMETER, 2 );

	exchange->MoveCorresponding( exchange, err );
	AddCorresponding( exchange );

	OUTFUNC()
};

void XCBuffer::SubCorresponding( XCBuffer* source ) {
	INFUNC( XCBuffer::SubCorresponding, APPDIC )

	int i, j;
	XCField* temp;

	// Check for valid parameters
	if (!source)
		RAISETOOLSERROR_1PARAM( ERR_INVALID_PARAMETER, 1 );

	APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "(%d-%s)  Sub corresponding from (%d-%s)", mydesc->number, mydesc->name, source->mydesc->number, source->mydesc->name );

	for (i=0; i<mydesc->ds_count; i++) {
		long mynumber = FieldList[i].number;

		APPDIC->ToolsTrace( TRC_BUFFER, TLV_HIGH, "  Source field: %s", FieldList[i].GetFieldName() );

		temp = source->FieldList;
		for (j=0; j<source->mydesc->ds_count; j++) {
			if (temp->number == mynumber) {		// We have a match, so move it over
				// Attempt to move the field, fail on invalid field.
				FieldList[i].Sub( temp );
			};
			temp++;
		};
	};

	OUTFUNC()
};




void XCBuffer::SubCorrespondingExchange( XCBuffer* source, XCBuffer* exchange, XCAppErrorHandler* err ) {
	INFUNC( XCBuffer::SubCorrespondingExchange, APPDIC )

	// Check for valid parameters
	if (!source)
		RAISETOOLSERROR_1PARAM( ERR_INVALID_PARAMETER, 1 );
	if (!exchange)
		RAISETOOLSERROR_1PARAM( ERR_INVALID_PARAMETER, 2 );

	exchange->MoveCorresponding( exchange, err );
	SubCorresponding( exchange );

	OUTFUNC()
};


void XCBuffer::SubFields(long parm1, long parm2, long result) {
	INFUNC( XCBuffer::SubFields, APPDIC )

	XCField* field1 = GetField( parm1 );
	XCField* field2 = GetField( parm2 );
	XCField* rfield = GetField( result );

	if (!field1)
		RAISETOOLSERROR_1PARAM( ERR_FLD_NOT_FOUND, field1 );
	if (!field2)
		RAISETOOLSERROR_1PARAM( ERR_FLD_NOT_FOUND, field2 );
	if (!rfield)
		RAISETOOLSERROR_1PARAM( ERR_FLD_NOT_FOUND, rfield );

	rfield->Sub( field1, field2 );

	OUTFUNC()
}




void XCBuffer::MultFields(long parm1, long parm2, long result ) {
	INFUNC( XCBuffer::MultFields, APPDIC )

	XCField* field1 = GetField( parm1 );
	XCField* field2 = GetField( parm2 );
	XCField* rfield = GetField( result );

	if (!field1)
		RAISETOOLSERROR_1PARAM( ERR_FLD_NOT_FOUND, field1 );
	if (!field2)
		RAISETOOLSERROR_1PARAM( ERR_FLD_NOT_FOUND, field2 );
	if (!rfield)
		RAISETOOLSERROR_1PARAM( ERR_FLD_NOT_FOUND, rfield );

	rfield->Mult( field1, field2 );

	OUTFUNC()
}



void XCBuffer::DivFields(long parm1, long parm2, long result ) {
	INFUNC( XCBuffer::DivFields, APPDIC )

	XCField* field1 = GetField( parm1 );
	XCField* field2 = GetField( parm2 );
	XCField* rfield = GetField( result );

	if (!field1)
		RAISETOOLSERROR_1PARAM( ERR_FLD_NOT_FOUND, field1 );
	if (!field2)
		RAISETOOLSERROR_1PARAM( ERR_FLD_NOT_FOUND, field2 );
	if (!rfield)
		RAISETOOLSERROR_1PARAM( ERR_FLD_NOT_FOUND, rfield );

	rfield->Div( field1, field2 );

	OUTFUNC()
}





int XCBuffer::AddConst( long Field, long value )
{
	INFUNC( XCBuffer::AddConst, APPDIC )

	XCField* tmp = GetField( Field );

	if (tmp)
		tmp->AddConst( value );

	OUTFUNCRET( 0 )
};




int XCBuffer::SubConst( long Field, long value )
{
	INFUNC( XCBuffer::SubConst, APPDIC )

	XCField* tmp = GetField( Field );

	if (tmp)
		tmp->SubConst( value );

	OUTFUNCRET( 0 )
};



int XCBuffer::MultConst( long Field, long value )
{
	INFUNC( XCBuffer::MultConst, APPDIC )

	XCField* tmp = GetField( Field );

	if (tmp)
		tmp->MultConst( value );

	OUTFUNCRET( 0 )
};



int XCBuffer::DivConst( long Field, long value )
{
	INFUNC( XCBuffer::DivConst, APPDIC )

	XCField* tmp = GetField( Field );

	if (tmp)
		tmp->DivConst( value );

	OUTFUNCRET( 0 )
};




void* XCBuffer::GetData() const {
	return data;
};


long XCBuffer::GetSize()	const {
	return mydesc->length;
};

long XCBuffer::GetSizeWithBLOBs() const {
	INFUNC( XCBuffer::GetSizeWithBLOBs, APPDIC )
	int i;
	XCField *temp;

	long size = mydesc->length;		// start with just the buffer length
	if( HasBLOB ) {			// see if we even need to find BLOBs
		for( i=0; i<GetFieldCount(); i++ ) {	// loop through and find BLOBs
			temp = &FieldList[i];
			if( temp->GetType() == XCBLOB ) 
				size += temp->GetBLOBSize();
		}
	}

	OUTFUNCRET( size )
};

long XCBuffer::GetFormatNumber() const {
	assert( mydesc );
	return mydesc->number;
};

long XCBuffer::GetChildFormat() const {
	if (mydesc)
		return mydesc->child_format;
	else
		return 0;
};

long XCBuffer::GetFieldCount() const {
	return mydesc->ds_count;
};







void XCBuffer::Validate() {
	INFUNC( XCBuffer::Validate, APPDIC )

	for (int i=0; i<mydesc->ds_count; i++)
		FieldList[i].Validate();

	OUTFUNC()
};



buffer_db_tables* XCBuffer::GetDatasetDescr() {
	INFUNC( XCBuffer::GetDatasetDescr, APPDIC );

	assert( mydesc );
	assert( mydesc->dd_offset );

	OUTFUNCRET( (buffer_db_tables*)APPDIC->FixOffset( mydesc->dd_offset ) );
};


/*
buffer_screen_defn* XCBuffer::GetScreenInfo( long term_type ) {
	INFUNC( XCBuffer::GetScreenInfo, APPDIC );

	assert( mydesc );
	assert( mydesc->scr_offset );

	buffer_screen_defn* temp = (buffer_screen_defn*)APPDIC->FixOffset( mydesc->scr_offset );
	int count = mydesc->scr_count;
	while (count && temp->term_type != term_type) {
		temp++;
		count--;
	};

	if (!count)
		temp = NULL;

	OUTFUNCRET( temp );
};
*/



void XCBuffer::SetReadOnly( bool mode ) {
	XCField* fld = FieldList;

	int count = GetFieldCount();
	while (count) {
		fld->read_only = mode;
		fld++;
		count--;
	};
};

void XCBuffer::SetContainsBLOB( bool cBLOB ) {
	HasBLOB = cBLOB;
};

int XCBuffer::GetMsgSize() {
	return GetSizeWithBLOBs();
};

void XCBuffer::PutIntoMessage( unsigned char *buffer, int maxsize ) {
	INFUNC( XCBuffer::PutIntoMessage, APPDIC )
	unsigned char *cursor;
	int NumFields, i;
	char* buffer_offset;
	XCField *tempfield;

	if( GetSizeWithBLOBs() > maxsize ) {
		printf("Not enough room for buffer in message!\n");
		return;							//should return error here.
	}

	memcpy( buffer, GetData(), GetSize() );			//now copy the buffer
													
	if( HasBLOB ) {									//make sure we need to waste our time
		cursor = buffer + GetSize();					//point to BLOB data
		NumFields = GetFieldCount();
		long BLOBSize;
		for( i=0; i<NumFields; i++ ) 					//loop through and add BLOBs
		{
			tempfield = GetFieldByIndex( i );
			if( tempfield->GetType() == XCBLOB ) 
			{
				if( tempfield->Valid_BLOB() ) 
				{
					BLOBSize = tempfield->GetBLOBSize();
					memcpy( cursor, tempfield->GetBLOB(), BLOBSize ); // copies the BLOB itself
					buffer_offset = tempfield->GetOffset();
					memcpy( (buffer + (buffer_offset - this->data) + sizeof( long )), &cursor, sizeof(long *) ); // changes the BLOB pointer to the new BLOB copy
					cursor += BLOBSize;
				} 
//				else
//				{
//					*(long *)(tempfield->GetOffset()) = 0;	// make sure size of BLOB is 0
//				}
			}
		}
	}

	OUTFUNC()
};


bool XCBuffer::ContainsBLOB() {
	return HasBLOB;
};


void XCBuffer::CheckFieldLengths( XCAppErrorHandler* errors ) {
	INFUNC( XCBuffer::CheckFieldLengths, APPDIC );
	for (int i=0; i<mydesc->ds_count; i++) {
		XCField* temp = GetFieldByIndex( i );
		if (temp->CalcStringSize() < temp->GetDSInformation()->min_length) {
			if (errors) {
				errors->Add( ERR_LESS_THAN_MIN_LENGTH, SEV_ERROR, temp->GetFieldNumber(), 1, temp );
			} else
				RAISEFIELDERROR_1PARAM( ERR_LESS_THAN_MIN_LENGTH, temp->GetFieldNumber(), temp );
		};
	};
	OUTFUNC();
};


//
//
//
ostream& operator<<( ostream& s, XCBuffer& str ) {
	s.write( (char*)str.GetData(), str.GetSize() );

	return s;
};




istream& operator>>( istream& s, XCBuffer& str ) {
	s.read( (char*)str.GetData(), str.GetSize() );

	return s;
};

